package main

import (
	"encoding/json"
	"html/template"
	"net/http"
	"time"

	"gofw-im/lib/logger"
	"gofw-im/lib/proto"

	"github.com/gorilla/websocket"
)

var (
	newline = []byte{'\n'}
	space   = []byte{' '}
)

func InitWebsocket() (err error) {
	logger.Info.Printf("size: %d", defaultServer.Options.ReadBufferSize)
	http.HandleFunc("/", serveHome)
	http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
		serveWs(w, r)
	})
	logger.Info.Printf("comet initWebsocket addr:%s", conf.Websocket.Bind)
	err = http.ListenAndServe(conf.Websocket.Bind, nil)
	return err

}

// serveWs handles websocket requests from the peer.
func serveWs(w http.ResponseWriter, r *http.Request) {
	var upgrader = websocket.Upgrader{
		ReadBufferSize:  defaultServer.Options.ReadBufferSize,
		WriteBufferSize: defaultServer.Options.WriteBufferSize,
	}
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return
	}

	var ch *Channel
	// 写入配置
	ch = NewChannel(defaultServer.Options.BroadcastSize)
	ch.conn = conn

	go defaultServer.writePump(ch)
	go defaultServer.readPump(ch)

}

func serveHome(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.Error(w, "Not found", http.StatusNotFound)
		return
	}
	if r.Method != "GET" {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
	t, err := template.ParseFiles("comet/home.html")
	if err != nil {
		logger.Error.Printf("serveHome err:%v", err)
		return
	}
	t.Execute(w, nil)
}

func (s *Server) readPump(ch *Channel) {
	// TODO: disconn
	defer func() {
		ch.conn.Close()
		s.Bucket(ch.uid).delCh(ch)

		// decrement online number
		if err := s.operator.Disconnect(&proto.DisconnArg{RoomId: ch.Room.Id, Uid: ch.uid}); err != nil {
			logger.Error.Printf("Disconnect err :%s", err)
		}
	}()

	ch.conn.SetReadLimit(int64(s.Options.MaxMessageSize))
	ch.conn.SetReadDeadline(time.Now().Add(s.Options.PongWait))
	ch.conn.SetPongHandler(func(string) error {
		ch.conn.SetReadDeadline(time.Now().Add(s.Options.PongWait))
		return nil
	})
	// TODO: BUG 不应该在此获取值，也不应该在此创建bucket、room、channel
	for {
		_, message, err := ch.conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				logger.Error.Printf("readPump ReadMessage err:%v", err)
				return
			}
		}
		if message == nil {
			return
		}
		connArg := new(proto.ConnArg)
		if err = json.Unmarshal(message, connArg); err != nil {
			logger.Error.Printf("message struct %b", connArg)
		}
		logger.Info.Printf("message :%s", message)
		connArg.ServerId = conf.Base.ServerId
		// TODO: 每发送一条消息都要鉴权？
		uid, err := s.operator.Connect(connArg)
		logger.Info.Printf("websocket uid:%s", uid)
		if err != nil {
			logger.Error.Printf("s.operator.Connect error %s", err)
			return
		}
		if uid == "" {
			logger.Error.Println("Invalid Auth ,uid empty")
			return
		}
		b := s.Bucket(uid)
		// TODO: 每读取到一条消息都要put？
		err = b.Put(uid, connArg.RoomId, ch)
		if err != nil {
			logger.Error.Printf("conn close err: %s", err)
			ch.conn.Close()
		}
		// test
		p := new(proto.Proto)
		p.Body = message
		ch.broadcast <- p
	}
}

func (s *Server) writePump(ch *Channel) {
	ticker := time.NewTicker(s.Options.PingPeriod)
	defer func() {
		ticker.Stop()
		ch.conn.Close()
	}()
	for {
		select {
		case message, ok := <-ch.broadcast:
			ch.conn.SetWriteDeadline(time.Now().Add(s.Options.WriteWait))
			if !ok {
				// The hub closed the channel.
				ch.conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			logger.Info.Printf("TextMessage :%v", websocket.TextMessage)
			ch.conn.WriteMessage(websocket.TextMessage, message.Body)
		case <-ticker.C:
			ch.conn.SetWriteDeadline(time.Now().Add(s.Options.WriteWait))
			logger.Info.Printf("websocket.PingMessage :%v", websocket.PingMessage)
			if err := ch.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				return
			}
		}
	}
}
